当 AI 开始自动思考和行动——让 GPT 左右互搏会发生什么
之前我只是觉得在某些场合用 GPT 来写代码很方便。但在 Code Interpreter 里,它生成代码后能立刻运行、并根据运行结果自行迭代,这感觉就大不一样了:太自动了!
- 自动加戏实现需求里没提但确实很合理的功能
- 自动酌情偷懒少写一些代码
- 自动添加单元测试
- 代码执行报错时自动发现“忘记了”的改动并自动修复
我差一点,就把标题写成“当 AI 开始自主思考和行动”
拿到 Code Interpreter 权限后我迫不及待想要写些带自指怪圈的代码让 GPT 烧脑,很快就撞墙了:在沙盒中的 Code Interpreter 不能访问网络、不能接受用户输入、不能安装新的模块、甚至不能调用它自己刚生成并保存到本地的代码文件。
在 GPT 中让运行的代码调用 GPT 来迭代生成更多代码并运行以调用 GPT 来迭代生成更多代码并运行以调用 GPT……如此循环往复的念头胎死腹中。
(当然,反过来,自己写一段代码调用 OpenAI 的API 接口来让 GPT 生成更多代码、执行这些代码以生成更多更多代码……这样的操作是可行的。但它和在 GPT 中运行代码还是有很大差异,后面的介绍中你会体会到。)
想想也是,如果同时允许访问网络和执行代码,谁晓得 GPT 会做出什么事情来呢。这个人类尚未理解透彻的怪兽还是得先在笼子里关段时间。
那么多限制下,还能玩啥呢?我想到了下棋:让 GPT 写个棋盘游戏自己和自己玩,看最终能进化出多厉害的策略。
GPT 自己和自己玩 Tic-Tac-Toe
先从最简单的 Tic-Tac-Toe 开始。我让它先实现了棋盘 Board 和 Game 类,再实现一个随机找空格下子的 RandomPlayer 类。一轮游戏后,我要求它写个更聪明的 Player。它立刻想出了 5 项策略,并一一实现。
在测试中,智能玩家果然九胜一平碾压式胜利。
我让 GPT 继续迭代算法,它又想了两个新招数,写出了更复杂的算法。
不过实验结果一点意思都没有,净是平局。正如 GPT 所说,这恰是井字棋游戏的特点。看来玩到这会儿也就到头了。
GPT 自己和自己玩五子棋
接下来我让 GPT 开发五子棋游戏。棋盘可复用,只需要写一个新的 Go5Game 类。让两个 RandomPlayer 对弈一轮后,我提出了迭代玩家算法的要求,GPT 给出了一套基本策略。
不出所料新的 SmartGo5Player 赢得了所有棋局。
让它如法炮制再迭代三轮:
继续迭代,SmartGo5Player2 以 10:0 完胜 SmartGo5Player。
继续迭代。这回的新玩家优势不明显了,6:4 险胜。
继续迭代。这回新玩家反而 4:6 败北了。
我尝试让它写一些更高级的算法,它想到了 minimax 和 alpha-beta 剪枝。
结果写出了一个智障玩家,用最傻的方式输了全部 10 局比赛。然后,就没有然后了。GPT 说更聪明的玩家所需的代码已经超出了当前问题的范围。
看来 GPT 的左右互搏自我迭代也只能到此为止。等什么时候解除了封印、它有了更大的内存和更强大的访问网络的能力后,再继续吧。
一些关于 GPT 写代码能力的小发现
1. GPT 会自己加戏,即便你没要求,它有时候也会多做一点,有时效果还不错。例如下图,代码运行的 STDOUT 无法输出彩色格式,它主动用 mathematica 的代码格式重画了一遍最终结果。
有时候添加的功能还挺让人惊艳,像一个能比产品经理还多想一步的好开发。例如下图,我只是让它实现“玩多次”的功能,它不但想到了循环调用之前的“玩一次”,还想到了玩多次可能需要的只是最终结果、于是添加了 show_detail 参数用于显示或隐藏中间过程。
2. GPT 也会酌情偷懒,并且告诉你“我可不是没想到我只是觉得现在没必要写”,或者“代码太长了所以我给你个简化版本”。
3. 当我给出的需求清晰时,GPT 的代码习惯很好,会主动写单元测试并根据单元测试的结果修复 bug。
4. GPT 有时候会犯一些很傻的错误,也会根据运行的出错信息自动分析原因并快速修正。
在絮絮叨叨解释原因的时候,它经常用的词语是“忘记”。一个 AI 会因什么而“忘记”事情又会因什么而再次想起?这是 AI 么?
一些教 GPT 写代码的小技巧
1. 一开始就要给 GPT 做好代码架构设计,否则后续改需求时 GPT 会把代码弄得一团糟。
例如我说“帮我写一个 tic-tac-toe 游戏”后 GPT 写的游戏能运行得不错,但仅仅对输出格式提了一些要求后,它就陷入不断自我修复的泥淖中。
改成下面帮助明确了类设计的清晰指令后,GPT 产出的代码更合理,可维护性就好多了。看来 GPT 还是需要人类工程师帮它更好地做架构设计。
2. 代码太长会导致自动截断然后运行出错。因此要记得要求 GPT 一步一步写代码并运行。Code Interpreter 背后是 Jupyter Notebook,一旦类或方法或变量在之前的 cell 里被定义/赋值了,后面的代码里就能用。因此把代码拆碎了分别运行,可能是突破 GPT token 长度限制的方法。
3. 有些时候因为一个小小的需求引发 GPT 对代码做了一个小小的但不合理的改动,导致运行时报错,然后 GPT 会非常自动地去做修复,不断地引发新的错误进行新的修复,直到把代码改得面目全非。为了避免这样的灾难,我们需要想在前边,提前告诉 GPT 应该怎么做。例如下图,我要求“让后续别的类改动时 play_single_game 方法都不需要改动”,GPT 写出来的函数里终于不再有 hard-code 的部分了,也就避免了后续的麻烦。( 这让我想起十几年前在微软刚开始带人时,明明看到新人的代码有很多问题,却无法说服他改造。我沮丧地跟老板抱怨,老板说——你在他已经写得差不多了才这么说,当然行不通。布置任务时你就需要 think ahead 呀。)
前些天 Kent 发推说他的技能 90% 被归零、 10% 被放大 1000 倍。上述几条小技巧,也属于 10% 的部分吧。
更多 GPT 功能的尝鲜文章
带上插件后的 ChatGPT 更惊艳!机器人和机器人对话的世界真的来了
加了插件的 ChatGPT 目前确实是中心,希望未来的机联网不是
能运行代码的 GPT 那是真的王炸呀